﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Management;
using System.Threading;

namespace MessageRouter.ServiceBroker.Activation
{
    internal sealed class ServiceBrokerMessageReaderWMIActivator : ServiceBrokerMessageReaderActivator
    {
        private readonly BackgroundWorker worker;
        private ManagementEventWatcher watcher;
        private AutoResetEvent workerStopped;
        private readonly TimeSpan timeout = TimeSpan.FromSeconds(20);

        public ServiceBrokerMessageReaderWMIActivator(ServiceBrokerEndpointAddress endpointAddress)
            : base(endpointAddress)
        {
            this.worker = new BackgroundWorker();
            this.worker.WorkerSupportsCancellation = true;
            this.worker.DoWork += WorkerProc;
            this.workerStopped = new AutoResetEvent(false);
        }

        private void WorkerProc(object sender, DoWorkEventArgs e)
        {
            try
            {
                string queryString = GetQueryString(this.EndpointAddress);
                string managementPath = GetManagementPath(this.EndpointAddress);
                WqlEventQuery query = new WqlEventQuery(queryString);
                using (this.watcher = new ManagementEventWatcher(query))
                {
                    ManagementScope scope = new ManagementScope(managementPath);
                    scope.Connect();

                    watcher.Scope = scope;
                    watcher.Options.Timeout = this.timeout;

                    while (!this.worker.CancellationPending)
                    {
                        try
                        {
                            if (watcher.WaitForNextEvent() != null)
                            {
                                OnActivation();
                            }
                        }
                        catch(ManagementException ex)
                        {
                            if (ex.ErrorCode != System.Management.ManagementStatus.Timedout)
                                throw;
                        }
                    }
                    this.workerStopped.Set();
                }
            }
            catch(Exception ex)
            {
                Trace.TraceError("ServiceBrokerMessageReaderWMIActivator failed with error: {0}", ex.Message);
            }
        }

        public override void Open()
        {
            this.worker.RunWorkerAsync();
        }

        public override void Close()
        {
            Trace.TraceInformation("try to shutdown ServiceBroker WMI Activator");
            this.worker.CancelAsync();
            this.workerStopped.WaitOne(this.timeout);
            Trace.TraceInformation("ServiceBroker WMI Activator shutdown completed");
        }

        private string GetManagementPath(ServiceBrokerEndpointAddress endpointAddress)
        {
            return string.Format(@"\\{0}\root\Microsoft\SqlServer\ServerEvents\{1}", endpointAddress.ServerName, endpointAddress.InstanceName);
        }

        private string GetQueryString(ServiceBrokerEndpointAddress endpointAddress)
        {
            return string.Format(@"select * from QUEUE_ACTIVATION where DatabaseName = '{0}' and SchemaName = '{1}' and ObjectType='QUEUE' and ObjectName='{2}'", endpointAddress.DatabaseName, endpointAddress.SchemaName, endpointAddress.QueueName);
        }
    }
}
